home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / rdcf.exe / CACHE.DOC < prev    next >
Encoding:
Text File  |  1993-01-15  |  14.1 KB  |  348 lines

  1.                     A Simple Reentrant Cache System
  2.                              Version 1.1
  3.  
  4.                        by Philip J. Erdelsky
  5.                        CompuServe 75746,3411
  6.                  InterNet 75746.3411@compuserve.com
  7.  
  8.                         September 15, 1992
  9.  
  10.                PUBLIC DOMAIN -- NO RESTRICTIONS ON USE
  11.  
  12. 0. What's New
  13. -------------
  14.  
  15. The following features were not in version 1.0:
  16.  
  17.   (1) The cache system keeps track of the number of empty, clean and dirty
  18.       sectors.
  19.  
  20.   (2) When flushing the cache, the cache system does not quit on the first
  21.       error. It continues flushing.
  22.  
  23. 1. Introduction
  24. ---------------
  25.  
  26. Many applications that use disks tend to read and write the same sectors
  27. repeatedly. A technique called caching can make such applications run much
  28. faster. With this technique, frequently used sectors are read into memory
  29. buffers when the application begins. The application then reads and writes the
  30. memory buffers. When the application terminates, the buffers are written to
  31. the disk. Since reading and writing memory buffers is much faster than reading
  32. or writing disk sectors, the application can run much faster. There are some
  33. problems with caching. For example, it can be difficult to determine which
  34. sectors will be used frequently. However, the technique is widely used because
  35. the improvement is often quite substantial.
  36.  
  37. This cache system is a simple one, designed to be placed between the Reentrant
  38. DOS-Compatible File System (RDCF) and a disk driver.
  39.  
  40. A single cache can be used for two or more drives, as long as they all have
  41. the same sector size. The cache system is not reentrant with respect to
  42. different drives in this case, so access to it must be serialized.
  43.  
  44. The cache system can handle as many separate caches as available memory
  45. permits. It is fully reentrant with respect to different caches. However,
  46. separate caches may not access the same drive.
  47.  
  48. The cache system calls the standard C functions malloc() and free() when a
  49. cache is being initialized or freed, so reentrancy of those functions is
  50. required at those times. However, it does no memory allocation or deallocation
  51. at other times.
  52.  
  53. The number of sectors in the cache and the size of each sector are determined
  54. at run time and need not be the same for all caches. The only limit to the
  55. number of sectors in the cache is available memory. A cached sector need not
  56. correspond to a single sector on disk, but it must be treated as such by
  57. functions that call and are called by the cache system.
  58.  
  59. Each cache can handle up to 32,768 drives, numbered from 0 to 32,767, and up to
  60. 65,536 sectors on each drive, numbered from 0 to 65,535. The use of larger
  61. numbers will produce numeric overflow and catastrophic failure. Drive numbers
  62. and sector numbers need not be consecutive.
  63.  
  64. No cache system can be optimal in every conceivable case, no matter how
  65. cleverly it is programmed. This cache system uses one of the simplest methods,
  66. and it is apparently adequate for some purposes. You are welcome to make your
  67. own improvements, but you must assume responsibility for the results if you
  68. make a mistake.
  69.  
  70. In this cache system, each cached sector may exist in any of three states:
  71.  
  72.      (1) An EMPTY sector bears no relation to any disk sector.
  73.  
  74.      (2) A CLEAN sector contains the same data as the corresponding disk
  75.          sector.
  76.  
  77.      (3) A DIRTY sector contains data which is to be written to the
  78.          corresponding disk sector but has not yet been written.
  79.  
  80. The cache system attempts to keep each sector in the cache as long as it can,
  81. and it uses the least-recently-used criterion when cached sectors must be
  82. reused. The process is explained in greater detail in Section 4.
  83.  
  84. The source code for the cache system consists of the file CACHE.C and the
  85. header file CACHE.H. The latter should be #included in any source file that
  86. calls on the cache system, because it contains prototypes for all cache
  87. functions and other necessary information.
  88.  
  89. The cache system calls on the following standard C functions:
  90.  
  91.      malloc()
  92.      free()
  93.      memcpy()
  94.  
  95. To prevent identifier conflicts, all publicly defined identifiers in the cache
  96. system begin with the characters "cache", "CACHE" or "_CACHE".
  97.  
  98.  
  99. 2. How the Cache Reads and Writes a Sector
  100. ------------------------------------------
  101.  
  102. The cache system does not read or write the disk directly. You must provide it
  103. with a pointer to a function that you have written for your implementation. The
  104. cache system then calls this function whenever it needs to read or write a disk
  105. sector. The format of the function call is as follows:
  106.  
  107.      e = (*drive_access)(write, drive, sector, buffer);
  108.  
  109.      int write;        nonzero (true) for a write operation; zero (false)
  110.                        for a read operation
  111.  
  112.      unsigned drive;   drive to be read or written
  113.  
  114.      unsigned sector;  sector to be read or written
  115.  
  116.      void *buffer;     address of memory buffer to receive or supply data
  117.  
  118.      unsigned e;       zero for a successful operation, or an
  119.                        implementation-defined nonzero error code
  120.  
  121. When the cache receives a nonzero value from this function, it aborts the cache
  122. operation and returns the value as its own functional value. The cache control
  123. block contains the drive and sector numbers of the offending operation in the
  124. members error_drive and error_sector, respectively. This is especially helpful
  125. for diagnostic purpose, because the error almost always occurs in a sector
  126. other than the one currently being accessed by the application.
  127.  
  128.  
  129. 3. Creating and Initializing a Cache
  130. ------------------------------------
  131.  
  132. The following function call creates and initializes a cache:
  133.  
  134.      q = cache_initialize(drive_access, number_of_sectors, sector_size);
  135.  
  136.      unsigned (*drive_access)();  pointer to function called by cache
  137.                                   to read or write a sector
  138.  
  139.      unsigned number_of_sectors;  number of sectors to be stored in cache
  140.                                   (must be at least 1)
  141.  
  142.      unsigned sector_size;        number of bytes in a sector (1-65,535)
  143.  
  144.      struct cache *q;             pointer to cache control block, or NULL
  145.                                   if there was insufficient memory
  146.  
  147. The function calls on malloc() to allocate memory for the cache storage. If
  148. malloc() returns NULL at any point in the process, the function calls free()
  149. to release any memory that it has allocated and returns a NULL pointer.
  150. Otherwise, it returns a pointer that can be passed to other cache functions.
  151. It marks all sectors in the cache as empty.
  152.  
  153. A sector size near the upper limit of 65,535 may cause numeric overflow and
  154. catastrophic failure if the implementation does not permit allocation of single
  155. objects larger than 65,535 bytes.
  156.  
  157.  
  158. 4. Reading or Writing Through the Cache
  159. ---------------------------------------
  160.  
  161. The heart of the cache system is the following function call:
  162.  
  163.      e = cache_access(q, write, drive, sector, buffer);
  164.  
  165.      struct cache *q;  pointer to cache control block received from
  166.                        cache_initialize()
  167.  
  168.      int write;        nonzero (true) for a write operation; zero (false)
  169.                        for a read operation
  170.  
  171.      unsigned drive;   drive to be read or written
  172.  
  173.      unsigned sector;  sector to be read or written
  174.  
  175.      void *buffer;     address of memory buffer to receive or supply data
  176.  
  177.      unsigned e;       zero for a successful operation, or an
  178.                        implementation-defined nonzero error code
  179.  
  180. This function writes or reads the specified sector to or from the specified
  181. drive, using the cache as follows:
  182.  
  183.     (1) If the specified sector is not already in the cache, a cache sector
  184.         is assigned to it as follows:
  185.  
  186.          (a) If there are still empty sectors in the cache, one of them is
  187.              assigned to the specified sector.
  188.  
  189.          (b) If there are no empty sectors, but there are some clean ones,
  190.              the least-recently used clean sector is marked as empty and
  191.              assigned to the specified sector.
  192.  
  193.          (c) In other cases, the least-recently used dirty sector is written
  194.              to disk, marked as empty, and assigned to the specified sector.
  195.  
  196.      (2) When reading,
  197.  
  198.          (a) If the sector is empty, it is read from the disk and marked as
  199.              clean.
  200.  
  201.          (b) The sector is then copied from the cache to the calling program's
  202.              buffer.
  203.  
  204.      (3) When writing, the calling program's buffer is copied to the cache and
  205.          the sector is marked as dirty. IT IS NOT WRITTEN TO DISK.
  206.  
  207.      (4) The sector is marked as the most recently used.
  208.  
  209. If an error is detected on an actual disk access, this function aborts the
  210. operation, puts the drive and sector numbers of the offending sector into
  211. q->error_drive and q->error sector, and returns the implementation-defined
  212. nonzero error code passed to it by (*drive_access)(). Otherwise, it returns
  213. zero.
  214.  
  215.  
  216. 5. Flushing the Cache
  217. ---------------------
  218.  
  219. Before a disk can be changed or removed, the dirty sectors must be written out.
  220. The following function call does that, either for a specified drive or for all
  221. drives:
  222.  
  223.      e = cache_flush(q, drive);
  224.  
  225.      struct cache *q;  pointer to cache control block received from
  226.                        cache_initialize()
  227.  
  228.      int drive;        drive to be flushed; if drive<0, all drives are
  229.                        flushed
  230.  
  231.      unsigned e;       zero for a successful operation, or an
  232.                        implementation-defined nonzero error code
  233.  
  234. If the drive number is nonnegative, this function writes all dirty sectors to
  235. the specified drive and marks them as clean. Sectors on other drives are not
  236. affected. If the drive number is negative, it writes all dirty sectors to all
  237. drives and marks them as clean.
  238.  
  239. The sectors are not written in any specified order, and their usage order is
  240. not changed.
  241.  
  242. If an error is detected while writing a sector, this function puts the drive
  243. and sector numbers of the offending sector into q->error_drive and q->error
  244. sector, and saves the implementation-defined nonzero error code passed to it by
  245. (*drive_access)(). Then in continues flushing. When it has finished flushing,
  246. it returns an error code, if any was received. Otherwise, it returns zero. If
  247. more than one error was detected, it returns last error code, drive and sector.
  248.  
  249. This function is implemented as a macro which generates a call on the function
  250. cache_flush_and_or_clear().
  251.  
  252.  
  253. 6. Clearing the Cache
  254. ---------------------
  255.  
  256. After a disk is changed, the cached sectors on that drive become invalid. The
  257. following function call informs the cache system of that fact:
  258.  
  259.      cache_clear(q, drive);
  260.  
  261.      struct cache *q;  pointer to cache control block received from
  262.                        cache_initialize()
  263.  
  264.      int drive;        drive to be cleared; if drive<0, all drives are
  265.                        cleared
  266.  
  267. If the drive number is nonnegative, this function marks all cached sectors on
  268. the specified drive as empty. Sectors on other drives are not affected. If the
  269. drive number is negative, it marks all cached sectors on all drives as empty.
  270.  
  271. THIS FUNCTION DOES NOT WRITE THE CACHED SECTORS TO DISK, EVEN IF THEY ARE
  272. DIRTY.
  273.  
  274. This function is implemented as a macro which generates a call on the function
  275. cache_flush_and_or_clear().
  276.  
  277.  
  278. 7. Flushing and Clearing the Cache
  279. ----------------------------------
  280.  
  281. Before a disk can be changed and removed, the dirty sectors must be written
  282. out, and the cached sectors on the drive must be marked as invalid. The
  283. following function call does that, either for a specified drive or for all
  284. drives:
  285.  
  286.      e = cache_flush_and_clear(q, drive);
  287.  
  288.      struct cache *q;  pointer to cache control block received from
  289.                        cache_initialize()
  290.  
  291.      int drive;        drive to be flushed and cleared; if drive<0, all
  292.                        drives are flushed and cleared
  293.  
  294.      unsigned e;       zero for a successful operation, or an
  295.                        implementation-defined nonzero error code
  296.  
  297. If the drive number is nonnegative, this function first finds all sectors
  298. assigned to the specified drive. It writes the dirty sectors to the specified
  299. drive and then marks both clean and dirty sectors as empty. Sectors on other
  300. drives are not affected. If the drive number is negative, it does the same
  301. thing to all sectors on all drives.
  302.  
  303. The sectors are not written in any specified order.
  304.  
  305. If an error is detected while writing a sector, this function puts the drive
  306. and sector numbers of the offending sector into q->error_drive and q->error
  307. sector, and saves the implementation-defined nonzero error code passed to it by
  308. (*drive_access)(). Then in continues flushing. When it has finished flushing,
  309. it returns an error code, if any was received. Otherwise, it returns zero. If
  310. more than one error was detected, it returns last error code, drive and sector.
  311.  
  312. This function is equivalent to calls on cache_flush() and cache_clear(), but it
  313. is more efficient because it makes only a single pass through the cache.
  314.  
  315. This function is implemented as a macro which generates a call on the function
  316. cache_flush_and_or_clear().
  317.  
  318.  
  319. 8. Freeing the Cache
  320. --------------------
  321.  
  322. The following function calls free() to release all memory allocated for a
  323. cache:
  324.  
  325.      cache_free(q);
  326.  
  327.      struct cache *q;  pointer to cache control block received from
  328.                        cache_initialize()
  329.  
  330.  
  331. 9. Sector Counts
  332. ----------------
  333.  
  334. The following cache control block members contain the current numbers of empty,
  335. clean and dirty sectors in the cache:
  336.  
  337.      unsigned q->empty;   number of empty sectors in cache
  338.      unsigned q->clean;   number of clean sectors in cache
  339.      unsigned q->dirty;   number of dirty sectors in cache
  340.  
  341.      struct cache *q;    pointer to cache control block received from
  342.                          cache_initialize()
  343.  
  344. In some applications you may want to use these numbers to decide when to flush
  345. the cache.
  346.  
  347. 
  348.